#pragma once

#include <iostream>
#include <string>

#include "log.hpp"

#include <cstring>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>

Log log;

class Share_Memory
{
    enum Share_Memory_Error
    {
        KEY_GET_ERROR = 1,
        SHARE_MEMORY_CREAT_ERROR = 2,
        SHARE_MEMORY_LOAD_ERROR = 3,
        SHARE_MEMORY_UNLOAD_ERROR = 4,
        SHARE_MEMORY_DELETE_ERROR = 5
    };

    static key_t get_key()
    {
        const std::string pathname("/home/qyc");
        const int proj_id = 0x6666;
        
        key_t key = ftok(pathname.c_str(), proj_id);
        if (key < 0)
        {
            log(Fatal, "ftok error: %s", strerror(errno));
            exit(KEY_GET_ERROR);
        }
        log(Info, "ftok success, key is 0x%x", key);
        return key;
    }
public:
    static int get(int flag = IPC_CREAT)
    {
        constexpr int SHARE_MEMORY_SIZE = 4096;

        key_t key = get_key();
        int shmid = shmget(key, SHARE_MEMORY_SIZE, flag);
        if (shmid < 0)
        {
            log(Fatal, "share memory creat error: %s", strerror(errno));
            exit(SHARE_MEMORY_CREAT_ERROR);
        }
        log(Info, "share memory creat success, shmid: %d", shmid);
        return shmid;
    }

    static int creat()
    {
        return get(IPC_CREAT | IPC_EXCL | 0666);
    }

    static char* load(int shmid)
    {
        char* ret = (char*)shmat(shmid, nullptr, 0);
        if (ret == (char*)-1)
        {
            log(Fatal, "share memory load error: %s", strerror(errno));
            exit(SHARE_MEMORY_LOAD_ERROR);
        }
        log(Info, "share memory load success");
        return ret;
    }

    static void unload(const void* shmaddr)
    {
        if (shmdt(shmaddr) < 0)
        {
            log(Fatal, "share memory unload error: %s", strerror(errno));
            exit(SHARE_MEMORY_UNLOAD_ERROR);
        }
        log(Info, "share memory unload success");
    }

    static void free(int shmid)
    {
        if (shmctl(shmid, IPC_RMID, nullptr) < 0)
        {
            log(Fatal, "share memory delete error: %s", strerror(errno));
            exit(SHARE_MEMORY_DELETE_ERROR);
        }
        log(Info, "share memory delete success, shmid: %d", shmid);
    }
};

#define FIFO_FILE "./fifo"
#define MODE 0664

class FIFO
{
    enum Fifo_State_Error
    {
        FIFO_CREATE_ERROR = 1,
        FIFO_DELETE_ERROR = 2,
        FIFO_OPEN_ERROR = 3
    };
public:
    FIFO(const char* filename = FIFO_FILE)
        : _fifo_path(filename)
    {
        int n = mkfifo(_fifo_path.c_str(), MODE);
        if (n < 0)
        {
            log(Fatal, "fifo creat error: %s", strerror(errno));
            exit(FIFO_CREATE_ERROR);
        }
    }

    static int open_read(const char* filename)
    {
        int fd = open(filename, O_RDONLY);
        if (fd < 0)
        {
            log(Fatal, "error string: %s", strerror(errno));
            exit(FIFO_OPEN_ERROR);
        }
        return fd;
    }
    static int open_write(const char* filename)
    {
        int fd = open(filename, O_WRONLY);
        if (fd < 0)
        {
            log(Fatal, "error string: %s", strerror(errno));
            exit(FIFO_OPEN_ERROR);
        }
        return fd;
    }

    ~FIFO()
    {
        int m = unlink(_fifo_path.c_str());
        if (m == -1)
        {
            log(Fatal, "fifo unlink error %s", strerror(errno));
            exit(FIFO_DELETE_ERROR);
        }
    }
private:
    std::string _fifo_path;

};